package org.paul.gotobplan.rest.helper;

import android.content.res.AssetManager;
import org.androidannotations.annotations.AfterInject;
import org.androidannotations.annotations.App;
import org.androidannotations.annotations.EBean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;

import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Socket;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import static org.paul.gotobplan.BuildConfig.CONNECT_TIMEOUT;
import static org.paul.gotobplan.BuildConfig.READ_TIMEOUT;


/**
 * 绕过https认证机制,所有https请求均不需要认证
 * 添加请求超时机制
 */
@EBean(scope = EBean.Scope.Singleton)
public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {

    private static final int THOUSAND = 1000;
    @App
    org.paul.gotobplan.App app;
    @AfterInject
    void afterInject(){
        setConnectTimeout(THOUSAND * CONNECT_TIMEOUT);
        setReadTimeout(THOUSAND * READ_TIMEOUT);
        try {
            avoidValidation();//绕过https验证
//            validationCustom();//访问由自定义证书的HTTPs站点 TODO
//            validationDefault();//默认访问由可信机构颁发证书的HTTPs站点 TODO
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
        try {
            if (!(connection instanceof HttpsURLConnection)) {
                throw new RuntimeException("An instance of HttpsURLConnection is expected");
            }

            HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
            httpsConnection.setHostnameVerifier(
//                    org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER
                new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        }
            );

            super.prepareConnection(httpsConnection, httpMethod);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void addValidation(HttpsURLConnection httpsURLConnection) {

    }

    /**
     * 访问由自定义证书的HTTPs站点
     */
    private void validationCustom()
            throws CertificateException, NoSuchAlgorithmException,
            KeyStoreException, NoSuchProviderException, IOException, KeyManagementException {
        // 获取keystore
        KeyStore keystore = certTrusted();
        SSLContext sslContext = SSLContext.getInstance("TLS");
        String algorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
        trustManagerFactory.init(keystore);
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(new CustomSSLSocketFactory(sslContext.getSocketFactory()));
    }

    // 根据证书文件生成keystore
    private KeyStore certTrusted()
            throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException {
        // 从资源文件中获取.cer证书文件
        AssetManager assetManager = app.getAssets();
        InputStream inputStream = assetManager.open("12306.cer");//replace your fileName here
        try {
            // 读取证书
            CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");
            java.security.cert.Certificate cer = cerFactory.generateCertificate(inputStream);
            // 创建一个证书库，并将证书导入证书库
            KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
            keyStore.load(null, null);
            keyStore.setCertificateEntry("12306", (java.security.cert.Certificate) cer);
            return keyStore;
        } finally {
            inputStream.close();
        }
    }

    /**
     * 访问由可信任机构颁发证书的https站点
     *
     * @throws KeyManagementException
     * @throws NoSuchAlgorithmException
     */
    private void validationDefault() throws KeyManagementException, NoSuchAlgorithmException {
        SSLContext localSSLContext = SSLContext.getInstance("TLS");
        localSSLContext.init(null, null, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(new CustomSSLSocketFactory(localSSLContext.getSocketFactory()));
    }

    /**
     * 绕过https 证书验证
     *
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    private void avoidValidation()
            throws NoSuchAlgorithmException, KeyManagementException {
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    }

                }
        };
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(new CustomSSLSocketFactory(sslContext.getSocketFactory()));

    }

    /**
     * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
     * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section)
     */
    private static class CustomSSLSocketFactory extends SSLSocketFactory {

        private final SSLSocketFactory delegate;

        public CustomSSLSocketFactory(SSLSocketFactory delegate) {
            this.delegate = delegate;
        }

        @Override
        public String[] getDefaultCipherSuites() {
            return delegate.getDefaultCipherSuites();
        }

        @Override
        public String[] getSupportedCipherSuites() {
            return delegate.getSupportedCipherSuites();
        }

        @Override
        public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose)
                throws IOException {
            final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
            return overrideProtocol(underlyingSocket);
        }

        @Override
        public Socket createSocket(final String host, final int port) throws IOException {
            final Socket underlyingSocket = delegate.createSocket(host, port);
            return overrideProtocol(underlyingSocket);
        }

        @Override
        public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort)
                throws
                IOException {
            final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
            return overrideProtocol(underlyingSocket);
        }

        @Override
        public Socket createSocket(final InetAddress host, final int port) throws IOException {
            final Socket underlyingSocket = delegate.createSocket(host, port);
            return overrideProtocol(underlyingSocket);
        }

        @Override
        public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort)
                throws
                IOException {
            final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
            return overrideProtocol(underlyingSocket);
        }

        private Socket overrideProtocol(final Socket socket) {
            if (!(socket instanceof SSLSocket)) {
                throw new RuntimeException("An instance of SSLSocket is expected");
            }
            ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"});
            return socket;
        }
    }

}
